/**
 * HTTP 请求工具
 * @description 基于 luch-request 封装的 HTTP 请求工具，提供统一的请求配置、拦截器和错误处理
 * @module utils/request
 * @see https://www.quanzhan.co/luch-request/guide/3.x/
 * @example
 * // 基础用法
 * import { get, post } from '@/utils/request'
 *
 * // GET 请求
 * const data = await get('/api/users')
 *
 * // POST 请求
 * const result = await post('/api/users', { name: 'test' })
 *
 * @example
 * // 自定义配置
 * get('/api/users', null, {
 *   custom: {
 *     loading: true,      // 显示加载提示
 *     auth: false,        // 不需要认证
 *   }
 * })
 */

import type { HttpData, HttpRequestConfig, HttpResponse } from 'luch-request'
import Request from 'luch-request'
import {
  API_BASE_URL,
  AUTH_TOKEN_KEY,
  REQUEST_LOADING,
  REQUEST_LOADING_MASK,
  REQUEST_LOADING_TEXT,
  REQUEST_NETWORK_ERROR_TEXT,
  REQUEST_TIMEOUT,
} from '@/config'
import { ResponseCode } from '@/enum'
import { useTokenStore, useUserStore } from '@/store'

// 自定义请求配置
interface CustomHttpRequestConfig extends HttpRequestConfig {
  custom?: {
    auth?: boolean // 是否需要认证
    loading?: boolean // 是否显示loading
  }
}

/**
 * 基础响应数据类型
 */
interface BaseRespData {
  code: number
  msg: string
  // header: Record<string, string> // 响应头
}

/**
 * 默认响应数据类型
 */
interface DefaultRespData<T> extends BaseRespData {
  data: T
}

/**
 * 分页响应数据类型
 */
export interface PaginationRespData<T> extends BaseRespData {
  rows: T[]
  total: number
}

/**
 * 请求实例
 */
const http = new Request({
  baseURL: API_BASE_URL,
  timeout: REQUEST_TIMEOUT,
  header: {
    'Content-Type': 'application/json',
    'source-client': 'miniapp',
  },
  custom: {
    loading: REQUEST_LOADING,
  },
})

// 请求拦截器
http.interceptors.request.use(
  (config: CustomHttpRequestConfig) => {
    const { custom = {}, header = {} } = config
    const { auth = true, loading } = custom

    // 处理认证
    const token = useTokenStore().token
    if (token && auth) header[AUTH_TOKEN_KEY] = `Bearer ${token}`
    config.header = header

    // 处理 loading
    loading && uni.showLoading({ title: REQUEST_LOADING_TEXT, mask: REQUEST_LOADING_MASK })
    return config
  },
  error => Promise.reject(error),
)

// 响应拦截器
http.interceptors.response.use(
  (response: HttpResponse) => {
    const { statusCode, data, config } = response
    const { custom = {} } = config
    const { loading } = custom

    // 处理 loading
    loading && uni.hideLoading()

    // 处理成功响应
    if (statusCode >= ResponseCode.SUCCESS && statusCode < 300) return response

    // 处理未授权
    if (statusCode === ResponseCode.UNAUTHORIZED) {
      const userStore = useUserStore()
      userStore.clearUser()
      uni.navigateTo({ url: '/pages/login/login' })
      return Promise.reject(response)
    }

    // 处理其他错误
    uni.showToast({
      icon: 'none',
      title: data.msg || '请求错误',
    })
    return Promise.reject(response)
  },
  error => {
    const { config } = error
    const { custom = {} } = config || {}
    const { loading } = custom

    // 处理 loading
    loading && uni.hideLoading()

    // 显示错误提示
    uni.showToast({
      icon: 'none',
      title: REQUEST_NETWORK_ERROR_TEXT,
    })
    return Promise.reject(error)
  },
)

/**
 * 请求响应类型
 *
 * - T 是响应数据的类型。
 * - 通过 extends 判断 T 是否包含 rows(数组类型) 和 total，包含则认为是分页数据，否则是普通数据。
 * - 使用 infer 关键字进行类型推导，推断数组元素的类型，用于 PaginationRespData<U>。
 */
type ResponseType<T> = T extends { rows: Array<infer U>; total: number }
  ? PaginationRespData<U>
  : DefaultRespData<T>

/**
 * 基础请求方法
 */
export const request = async <T>(config: CustomHttpRequestConfig): Promise<ResponseType<T>> =>
  (await http.request(config)).data as ResponseType<T>

/**
 * 请求方法配置类型
 */
type RequestMethodConfig = Omit<CustomHttpRequestConfig, 'url' | 'method'>

// 导出常用的请求方法
export const get = <T>(url: string, config?: RequestMethodConfig) =>
  request<T>({ ...config, url, method: 'GET' })

export const post = <T>(url: string, data?: HttpData, config?: RequestMethodConfig) =>
  request<T>({ ...config, url, method: 'POST', data })

export const put = <T>(url: string, data?: HttpData, config?: RequestMethodConfig) =>
  request<T>({ ...config, url, method: 'PUT', data })

export const del = <T>(url: string, config?: RequestMethodConfig) =>
  request<T>({ ...config, url, method: 'DELETE' })

export default http
